home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / network / admin / xinetd.2 / xinetd / xinetd.2.1.7-linux.4 / libs / src / sio / sio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-09-10  |  15.0 KB  |  691 lines

  1. /*
  2.  * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis
  3.  * All rights reserved.  The file named COPYRIGHT specifies the terms 
  4.  * and conditions for redistribution.
  5.  */
  6.  
  7. static char RCSid[] = "$Id: sio.c,v 1.3 1995/09/10 16:40:28 chuck Exp $" ;
  8. static char sio_version[] = VERSION ;
  9.  
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12.  
  13. #include "sio.h"
  14. #include "impl.h"
  15.  
  16. #ifdef EVENTS
  17. #include "events.h"
  18. #endif
  19.  
  20. #if defined(__FreeBSD__) || defined(__bsdi__) || defined(__NetBSD__)
  21. PRIVATE char *sio_memscan( char *from, int how_many, register char ch ) ;
  22. #endif
  23.  
  24. /*
  25.  * SIO WRITE FUNCTIONS: Swrite, Sputc
  26.  */
  27.  
  28. /*
  29.  * Stream write call: arguments same as those of write(2)
  30.  */
  31. int Swrite( fd, addr, nbytes )
  32.     int fd ;
  33.     register char *addr ;
  34.     register int nbytes ;
  35. {
  36.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  37.     register __sio_od_t *odp = ODP( dp ) ;
  38.     register int b_transferred ;
  39.     register int b_avail ;
  40.     int total_b_transferred ;
  41.     int b_written ;
  42.     int b_in_buffer ;
  43.  
  44. #ifdef EVENTS
  45.     EVENT( fd, EV_SWRITE ) ;
  46. #endif
  47.  
  48.     IO_SETUP( fd, dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ;
  49.     ASSERT( odp->start <= odp->nextb && odp->nextb <= odp->buf_end ) ;
  50.  
  51.     b_avail = odp->buf_end - odp->nextb ;
  52.     b_transferred = MIN( nbytes, b_avail ) ;
  53.     sio_memcopy( addr, odp->nextb, b_transferred ) ;
  54.     odp->nextb += b_transferred ;
  55.  
  56.     /*
  57.      * check if we are done
  58.      */
  59.     if ( b_transferred == nbytes )
  60.         return( b_transferred ) ;
  61.  
  62.     /*
  63.      * at this point we know that the buffer is full
  64.      */
  65.     b_in_buffer = odp->buf_end - odp->start ;
  66.     b_written = __sio_writef( odp, fd ) ;
  67.     if ( b_written != b_in_buffer )
  68.         return( (b_written >= nbytes) ? nbytes : b_written ) ;
  69.     
  70.     total_b_transferred = b_transferred ;
  71.     addr += b_transferred ;
  72.     nbytes -= b_transferred ;
  73.  
  74.     for ( ;; )
  75.     {
  76.         b_transferred = MIN( nbytes, odp->buffer_size ) ;
  77.         sio_memcopy( addr, odp->nextb, b_transferred ) ;
  78.         odp->nextb += b_transferred ;
  79.         nbytes -= b_transferred ;
  80.         if ( nbytes == 0 )
  81.         {
  82.             total_b_transferred += b_transferred ;
  83.             break ;
  84.         }
  85.         /*
  86.          * the buffer is full
  87.          */
  88.         b_written = __sio_writef( odp, fd ) ;
  89.         if ( b_written != odp->buffer_size )
  90.         {
  91.             if ( b_written != SIO_ERR )
  92.             {
  93.                 total_b_transferred += b_written ;
  94.                 odp->nextb += b_written ;
  95.             }
  96.             break ;
  97.         }
  98.         /*
  99.          * everything is ok
  100.          */
  101.         total_b_transferred += b_transferred ;
  102.         addr += b_transferred ;
  103.     }
  104.     return( total_b_transferred ) ;
  105. }
  106.  
  107.  
  108. /*
  109.  * Add a character to a file
  110.  */
  111. int Sputc( fd, c )
  112.     int fd ;
  113.     char c ;
  114. {
  115.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  116.     register __sio_od_t *odp = ODP( dp ) ;
  117.  
  118. #ifdef EVENTS
  119.     EVENT( fd, EV_SPUTC ) ;
  120. #endif
  121.     
  122.     IO_SETUP( fd, dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ;
  123.     ASSERT( odp->start <= odp->nextb && odp->nextb <= odp->buf_end ) ;
  124.  
  125.     /*
  126.      * The following is a weak check since we should really be
  127.      * checking that nextb == buf_end (it would be an error for
  128.      * nextb to exceed buf_end; btw, the assertion above, when
  129.      * enabled makes sure this does not occur).
  130.      *
  131.      * NOTE: __sio_writef NEVER uses data beyond the end of buffer.
  132.      */
  133.     if ( odp->nextb >= odp->buf_end )
  134.     {
  135.         int b_in_buffer = odp->buf_end - odp->start ;
  136.  
  137.         /*
  138.          * There is nothing we can do if __sio_writef does not manage
  139.          * to write the whole buffer
  140.          */
  141.         if ( __sio_writef( odp, fd ) != b_in_buffer )
  142.             return( SIO_ERR ) ;
  143.     }
  144.     *odp->nextb++ = c ;
  145.     if ( __SIO_MUST_FLUSH( *odp, c ) && __sio_writef( odp, fd ) == SIO_ERR )
  146.         return( SIO_ERR ) ;
  147.     return ( c ) ;
  148. }
  149.  
  150.  
  151.  
  152. /*
  153.  * SIO READ FUNCTIONS
  154.  */
  155.  
  156. /*
  157.  * Stream read call: arguments same as those of read(2)
  158.  */
  159. int Sread( fd, addr, nbytes )
  160.     int fd ;
  161.     char *addr ;
  162.     int nbytes ;
  163. {
  164.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  165.     register __sio_id_t *idp = IDP( dp ) ;
  166.     register int b_transferred ;
  167.     int b_read ;
  168.     int total_b_transferred ;
  169.     int b_left ;
  170.  
  171. #ifdef EVENTS
  172.     EVENT( fd, EV_SREAD ) ;
  173. #endif
  174.  
  175.     IO_SETUP( fd, dp, __SIO_INPUT_STREAM, SIO_ERR ) ;
  176.     ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  177.  
  178.     b_left = idp->end - idp->nextb ;
  179.     b_transferred = MIN( b_left, nbytes ) ;
  180.     sio_memcopy( idp->nextb, addr, b_transferred ) ;
  181.     idp->nextb += b_transferred ;
  182.     if ( b_transferred == nbytes )
  183.         return( b_transferred ) ;
  184.     
  185.     nbytes -= b_transferred ;
  186.     total_b_transferred = b_transferred ;
  187.     addr += b_transferred ;
  188.  
  189.     do
  190.     {
  191.         b_read = __sio_readf( idp, fd ) ;
  192.         switch ( b_read )
  193.         {
  194.             case SIO_ERR:
  195.                 if ( total_b_transferred == 0 )
  196.                     return( SIO_ERR ) ;
  197.                 /* FALL THROUGH */
  198.             
  199.             case 0:
  200.                 return( total_b_transferred ) ;
  201.         }
  202.             
  203.         b_transferred = MIN( b_read, nbytes ) ;
  204.         sio_memcopy( idp->nextb, addr, b_transferred ) ;
  205.         addr += b_transferred ;
  206.         idp->nextb += b_transferred ;
  207.         total_b_transferred += b_transferred ;
  208.         nbytes -= b_transferred ;
  209.     }
  210.     while ( nbytes && b_read == idp->buffer_size ) ;
  211.     return( total_b_transferred ) ;
  212. }
  213.  
  214.  
  215.  
  216. /*
  217.  * Read a line from a file
  218.  * Returns a pointer to the beginning of the line or NULL
  219.  */
  220. char *Srdline( fd )
  221.     int fd ;
  222. {
  223.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  224.     register __sio_id_t *idp = IDP( dp ) ;
  225.     register char *cp ;
  226.     register char *line_start ;
  227.     register int b_left ;
  228.     register int extension ;
  229.  
  230. #ifdef EVENTS
  231.     EVENT( fd, EV_SRDLINE ) ;
  232. #endif
  233.  
  234.     IO_SETUP( fd, dp, __SIO_INPUT_STREAM, NULL ) ;
  235.     ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  236.  
  237. #ifdef HAS_MMAP
  238.     if ( idp->memory_mapped && __sio_switch( idp, fd ) == FAILURE )
  239.         return( NULL ) ;
  240. #endif
  241.  
  242.     b_left = idp->end - idp->nextb ;
  243.     /*
  244.      * Look for a '\n'. If the search fails, extend the buffer
  245.      * and search again (the extension is performed by copying the
  246.      * bytes that were searched to the auxiliary buffer and reading 
  247.      * new input in the main buffer).
  248.      * If the new input still does not contain a '\n' and there is
  249.      * more space in the main buffer (this can happen with network
  250.      * connections), read more input until either the buffer is full
  251.      * or a '\n' is found.
  252.      * Finally, set cp to point to the '\n', and line_start to
  253.      * the beginning of the line
  254.      */
  255.     if ( b_left && ( cp = sio_memscan( idp->nextb, b_left, '\n' ) ) != NULL )
  256.     {
  257.         line_start = idp->nextb ;
  258.         idp->nextb = cp + 1 ;
  259.     }
  260.     else
  261.     {
  262.         extension = __sio_extend_buffer( idp, fd, b_left ) ;
  263.         if ( extension > 0 )
  264.         {
  265.             ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  266.  
  267.             line_start = idp->start ;
  268.             cp = sio_memscan( idp->nextb, extension, '\n' ) ;
  269.             if ( cp != NULL )
  270.                 idp->nextb = cp + 1 ;
  271.             else
  272.                 for ( ;; )
  273.                 {
  274.                     idp->nextb = idp->end ;
  275.                     extension = __sio_more( idp, fd ) ;
  276.                     if ( extension > 0 )
  277.                     {
  278.                         cp = sio_memscan( idp->nextb, extension, '\n' ) ;
  279.                         if ( cp == NULL )
  280.                             continue ;
  281.                         idp->nextb = cp + 1 ;
  282.                         break ;
  283.                     }
  284.                     else
  285.                     {
  286.                         /*
  287.                          * If there is spare room in the buffer avoid trashing
  288.                          * the last character
  289.                          */
  290.                         if ( idp->end < &idp->buf[ idp->buffer_size ] )
  291.                             cp = idp->end ;
  292.                         else
  293.                             cp = &idp->buf[ idp->buffer_size - 1 ] ;
  294.                         break ;
  295.                     }
  296.                 }
  297.         }
  298.         else                    /* buffer could not be extended */
  299.             if ( b_left == 0 )
  300.             {
  301.                 /*
  302.                  * Set errno to 0 if EOF has been reached
  303.                  */
  304.                 if ( extension == 0 )
  305.                     errno = 0 ;
  306.                 return( NULL ) ;
  307.             }
  308.             else
  309.             {
  310.                 line_start = idp->start ;
  311.                 cp = idp->end ;
  312.                 /*
  313.                  * By setting idp->nextb to be equal to idp->end,
  314.                  * subsequent calls to Srdline will return NULL because
  315.                  * __sio_extend_buffer will be invoked and it will return 0.
  316.                  */
  317.                 idp->nextb = idp->end ;
  318.             }
  319.     }
  320.     *cp = NUL ;
  321.     idp->line_length = cp - line_start ;
  322.     return( line_start ) ;
  323. }
  324.  
  325.  
  326. /*
  327.  * Get a character from a file
  328.  */
  329. int Sgetc( fd )
  330.     int fd ;
  331. {
  332.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  333.     register __sio_id_t *idp = IDP( dp ) ;
  334.  
  335. #ifdef EVENTS
  336.     EVENT( fd, EV_SGETC ) ;
  337. #endif
  338.  
  339.     IO_SETUP( fd, dp, __SIO_INPUT_STREAM, SIO_ERR ) ;
  340.     ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  341.     if ( idp->nextb >= idp->end )
  342.     {
  343.         register int b_read = __sio_readf( idp, fd ) ;
  344.  
  345.         if ( b_read == 0 )
  346.             return( SIO_EOF ) ;
  347.         else if ( b_read == SIO_ERR )
  348.             return( SIO_ERR ) ;
  349.     }
  350.     return( (int) *idp->nextb++ ) ;
  351. }
  352.  
  353.  
  354. char *Sfetch( fd, lenp )
  355.     int fd ;
  356.     long *lenp ;
  357. {
  358.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  359.     register __sio_id_t *idp = IDP( dp ) ;
  360.     register int b_read ;
  361.     register char *p ;
  362.  
  363. #ifdef EVENTS
  364.     EVENT( fd, EV_SFETCH ) ;
  365. #endif
  366.  
  367.     IO_SETUP( fd, dp, __SIO_INPUT_STREAM, NULL ) ;
  368.     ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  369.     if ( idp->nextb >= idp->end )
  370.     {
  371.         b_read = __sio_readf( idp, fd ) ;
  372.         if ( b_read == SIO_ERR )
  373.             return( NULL ) ;
  374.         if ( b_read == 0 )
  375.         {
  376.             errno = 0 ;
  377.             return( NULL ) ;
  378.         }
  379.     }
  380.     *lenp = idp->end - idp->nextb ;
  381.     p = idp->nextb ;
  382.     idp->nextb = idp->end ;
  383.     return( p ) ;
  384. }
  385.  
  386.  
  387.  
  388. /*
  389.  * SIO CONTROL FUNCTIONS
  390.  */
  391.  
  392. /*
  393.  * Undo the last Srdline or Sgetc
  394.  */
  395. int Sundo( fd, type )
  396.     int fd ;
  397.     int type ;
  398. {
  399.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ; 
  400.     register __sio_id_t *idp = IDP( dp ) ;
  401.     int retval = 0 ;
  402.  
  403. #ifdef EVENTS
  404.     EVENT( fd, EV_SUNDO ) ;
  405. #endif
  406.  
  407.     CONTROL_SETUP( dp, __SIO_INPUT_STREAM, SIO_ERR ) ;
  408.  
  409.     /*
  410.      * Undo works only for fd's used for input
  411.      */
  412.     if ( dp->stream_type != __SIO_INPUT_STREAM )
  413.         return( SIO_ERR ) ;
  414.  
  415.     /*
  416.      * Check if the operation makes sense; if so, do it, otherwise ignore it
  417.      */
  418.     switch ( type )
  419.     {
  420.         case SIO_UNDO_LINE:
  421.             if ( idp->nextb - idp->line_length > idp->start )
  422.             {
  423.                 *--idp->nextb = '\n' ;
  424.                 idp->nextb -= idp->line_length ;
  425.             }
  426.             ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  427.             break ;
  428.         
  429.         case SIO_UNDO_CHAR:
  430.             if ( idp->nextb > idp->start )
  431.                 idp->nextb-- ;
  432.             ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
  433.             break ;
  434.         
  435.         default:
  436.             retval = SIO_ERR ;
  437.             break ;
  438.     }
  439.     return( retval ) ;
  440. }
  441.  
  442.  
  443. /*
  444.  * Flush the buffer associated with the given file descriptor
  445.  * The special value, SIO_FLUSH_ALL flushes all buffers
  446.  *
  447.  * Return value:
  448.  *            0 :  if fd is SIO_FLUSH_ALL or if the flush is successful
  449.  *        SIO_ERR: if fd is not SIO_FLUSH_ALL and
  450.  *                                the flush is unsuccessful
  451.  *                            or the descriptor is not initialized or it is not 
  452.  *                                an output descriptor
  453.  */
  454. int Sflush( fd )
  455.     int fd ;
  456. {
  457.    register __sio_descriptor_t *dp ;
  458.    int b_in_buffer ;
  459.  
  460. #ifdef EVENTS
  461.     EVENT( fd, EV_SFLUSH ) ;
  462. #endif
  463.  
  464.    if ( fd == SIO_FLUSH_ALL )
  465.    {
  466.       for ( fd = 0, dp = __sio_descriptors ;
  467.                 fd < N_SIO_DESCRIPTORS ;
  468.                 dp++, fd++ )
  469.          if ( DESCRIPTOR_INITIALIZED( dp ) &&
  470.                             dp->stream_type == __SIO_OUTPUT_STREAM )
  471.             (void) __sio_writef( ODP( dp ), fd ) ;
  472.       return( 0 ) ;
  473.    }
  474.    else
  475.    {
  476.       dp = &__sio_descriptors[ fd ] ;
  477.  
  478.         CONTROL_SETUP( dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ;
  479.       b_in_buffer = ODP( dp )->nextb - ODP( dp )->start ;
  480.       if ( __sio_writef( ODP( dp ), fd ) != b_in_buffer )
  481.          return( SIO_ERR ) ;
  482.       else
  483.          return( 0 ) ;
  484.    }
  485. }
  486.  
  487.  
  488. /*
  489.  * Close the file descriptor. This call is provided because
  490.  * a file descriptor may be closed and then reopened. There is
  491.  * no easy way for SIO to identify such a situation, so Sclose
  492.  * must be used.
  493.  *
  494.  * Sclose invokes Sdone which finalizes the buffer.
  495.  * There is no SIO_CLOSE_ALL value for fd because such a thing
  496.  * would imply that the program will exit very soon, therefore
  497.  * the closing of all file descriptors will be done in the kernel
  498.  * (and the finalization will be done by the finalization function
  499.  * NOTE: not true if the OS does not support a finalization function)
  500.  *
  501.  * There is no need to invoke SETUP; Sdone will do it.
  502.  */
  503. int Sclose( fd )
  504.     int fd ;
  505. {
  506. #ifdef EVENTS
  507.     EVENT( fd, EV_SCLOSE ) ;
  508. #endif
  509.  
  510.     if ( __SIO_FD_INITIALIZED( fd ) )
  511.         if ( Sdone( fd ) == SIO_ERR )
  512.             return( SIO_ERR ) ;
  513.     return( close( fd ) ) ;
  514. }
  515.  
  516.  
  517.  
  518. /*
  519.  * Tie the file descriptor in_fd to the file descriptor out_fd
  520.  * This means that whenever a read(2) is done on in_fd, it is
  521.  * preceded by a write(2) on out_fd.
  522.  * Why this is nice to have:
  523.  *     1) When used in filters it maximizes concurrency
  524.  *        2) When the program prompts the user for something it forces
  525.  *            the prompt string to be displayed even if it does not
  526.  *            end with a '\n' (which would cause a flush).
  527.  * In this implementation, out_fd cannot be a regular file.
  528.  * This is done to avoid non-block-size write's.
  529.  * The file descriptors are initialized if that has not been done
  530.  * already. If any of them is initialized, it must be for the appropriate
  531.  * stream type (input or output).
  532.  *
  533.  * NOTE: the code handles correctly the case when in_fd == out_fd
  534.  */
  535. int Stie( in_fd, out_fd )
  536.     int in_fd, out_fd ;
  537. {
  538.     struct stat st ;
  539.     register __sio_descriptor_t *dp ;
  540.     int was_initialized ;
  541.     boolean_e failed = NO ;
  542.  
  543. #ifdef EVENTS
  544.     EVENT( in_fd, EV_STIE ) ;
  545. #endif
  546.  
  547.     /*
  548.      * Check if the out_fd is open
  549.      */
  550.     if ( fstat( out_fd, &st ) == -1 )
  551.         return( SIO_ERR ) ;
  552.  
  553.     /*
  554.      * If the out_fd refers to a regular file, the request is silently ignored
  555.      */
  556.     if ( ( st.st_mode & S_IFMT ) == S_IFREG )
  557.         return( 0 ) ;
  558.     
  559.     dp = &__sio_descriptors[ in_fd ] ;
  560.     was_initialized = dp->initialized ;        /* remember if it was initialized */
  561.     IO_SETUP( in_fd, dp, __SIO_INPUT_STREAM, SIO_ERR ) ;
  562.  
  563.     /*
  564.      * Perform manual initialization of out_fd to avoid leaving in_fd
  565.      * initialized if the initialization of out_fd fails.
  566.      * If out_fd is initialized, check if it is used for output.
  567.      * If it is not initialized, initialize it for output.
  568.      */
  569.     dp = &__sio_descriptors[ out_fd ] ;
  570.     if ( DESCRIPTOR_INITIALIZED( dp ) )
  571.     {
  572.         if ( dp->stream_type != __SIO_OUTPUT_STREAM )
  573.         {
  574.             failed = YES ;
  575.             errno = EBADF ;
  576.         }
  577.     }
  578.     else
  579.         if ( __sio_init( dp, out_fd, __SIO_OUTPUT_STREAM ) == SIO_ERR )
  580.             failed = YES ;
  581.  
  582.     if ( failed == NO )
  583.     {
  584.         __SIO_ID( in_fd ).tied_fd = out_fd ;
  585.         return( 0 ) ;
  586.     }
  587.     else
  588.     {
  589.         /*
  590.          * We failed. If we did any initialization, undo it
  591.          */
  592.         if ( ! was_initialized )
  593.         {
  594.             int save_errno = errno ;
  595.  
  596.             (void) Sdone( in_fd ) ;
  597.             errno = save_errno ;
  598.         }
  599.         return( SIO_ERR ) ;
  600.     }
  601. }
  602.  
  603.  
  604. /*
  605.  * Untie a file descriptor
  606.  */
  607. int Suntie( fd )
  608.     int fd ;
  609. {
  610.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  611.  
  612. #ifdef EVENTS
  613.     EVENT( fd, EV_SUNTIE ) ;
  614. #endif
  615.  
  616.     CONTROL_SETUP( dp, __SIO_INPUT_STREAM, SIO_ERR ) ;
  617.     
  618.     if ( IDP( dp )->tied_fd != SIO_NO_TIED_FD )
  619.     {
  620.         IDP( dp )->tied_fd = SIO_NO_TIED_FD ;
  621.         return( 0 ) ;
  622.     }
  623.     else
  624.     {
  625.         errno = EBADF ;
  626.         return( SIO_ERR ) ;
  627.     }
  628. }
  629.  
  630.  
  631. /*
  632.  * Changes the type of buffering on the specified descriptor.
  633.  * As a side-effect, it initializes the descriptor as an output stream.
  634.  */
  635. int Sbuftype( fd, type )
  636.     int fd, type ;
  637. {
  638.     register __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
  639.  
  640. #ifdef EVENTS
  641.     EVENT( fd, EV_SBUFTYPE ) ;
  642. #endif
  643.  
  644.     /*
  645.      * Check for a valid type
  646.      */
  647.     if ( type != SIO_LINEBUF && type != SIO_FULLBUF && type != SIO_NOBUF )
  648.     {
  649.         errno = EINVAL ;
  650.         return( SIO_ERR ) ;
  651.     }
  652.  
  653.     IO_SETUP( fd, dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ;
  654.     ODP( dp )->buftype = type ;
  655.     return( 0 ) ;
  656. }
  657.  
  658.  
  659. #ifndef sio_memscan
  660.  
  661. PRIVATE char *sio_memscan( from, how_many, ch )
  662.    char *from ;
  663.    int how_many ;
  664.    register char ch ;
  665. {
  666.    register char *p ;
  667.    register char *last = from + how_many ;
  668.  
  669.    for ( p = from ; p < last ; p++ )
  670.       if ( *p == ch )
  671.          return( p ) ;
  672.       return( 0 ) ;
  673. }
  674.  
  675. #endif    /* sio_memscan */
  676.  
  677.  
  678. #ifdef NEED_MEMCOPY
  679.  
  680. void __sio_memcopy( from, to, nbytes )
  681.    register char *from, *to ;
  682.    register int nbytes ;
  683. {
  684.    while ( nbytes-- )
  685.       *to++ = *from++ ;
  686. }
  687.  
  688. #endif /* NEED_MEMCOPY */
  689.  
  690.  
  691.